public class ThreadTest {
    // 通过这个代码来演示，多线程和单线程，效率的提升
    public static void main(String[] args) {
        // 假设我们当前有两个变量，我们需要把两个变量 各自自增 1000w 次（典型的 CPU 密集型案例）
        // 可以一个线程 先针对 a 自增 然后再针对 b 自增
        // 也可以用两个线程分别对a和b自增

        //串行版本
        //serial(); // 运行结果为4789ms

        // 并发版本
        concurrency(); //运行结果为2786ms

        // 因此 此处使用的两个线程并发执行，时间确实缩短的很明显了（9000 -> 5000）
        // 但是为啥不是正好缩短一半？ 因为多线程可以更充分的利用到多核心cpu的资源，此时的thread1和thread2 一定是分布在两个cpu上执行
        // 但不能保证他俩一定是并行执行，而不是并发执行
        // 另一方面，线程调度自身也是有时间消耗的
        // 虽然缩短的不是50%但是仍然还是很明显的，仍热还有意义的

        // 在CPU密集中
        // 不是说使用多线程，就一定能够提升效率
        // 1.是否是多核
        // 2.当前核心是否空闲（如果cpu这些核心已经都满载了，这时候启动更多线程也没有用了）

        // 在IO密集中也有作用的
        // 比如在日常生活中经常看到“程序未响应”
        // （程序进行了一些耗时的IO操作，阻塞了界面相应）
        // 这种情况下使用多线程也是可以有效改善的
        
    }
    
    // 串行执行一个方法，一个线程来完成
    public static void serial(){
        // 为了衡量代码的执行速度，加上个及时的操作
        // 衡量执行时间的代码，让其跑的久一点，不是坏事，跑的久，误差就笑
        // （线程调度自身也是有时间开销的），运算的任务量越大，线程调度的开销相比之下就不是很明显了，从而就可以忽略不计了
        //currentTimeMillis 获取到当前系统的 ms 级时间戳
        long beg = System.currentTimeMillis();
        long a = 0;
        for (long i = 0; i < 100_0000_0000L; i++) { //自增一百亿次， _表示分割符的意思，要不0太多了就看花眼了，_在哪加就可以
            a++;
        }
        long b = 0;
        for (long i = 0; i < 100_0000_0000L; i++) {
            b++;
        }

        long end = System.currentTimeMillis();
        System.out.println("执行时间： "+(end-beg)+"ms");
    }

    // 这里写一个并发版本的
    public static  void  concurrency() {
        // 使用两个线程 来分别完成两个自增

        // 第一个线程
        Thread thread1 = new Thread(()->{
            long a = 0;
            for (long i = 0; i < 100_0000_0000L; i++) {
                a++;
            }
        });


        //第二个线程
        Thread thread2 = new Thread(()->{
            long b = 0;
            for (long i = 0; i < 100_0000_0000L; i++) {
                b++;
            }
        });

        // 启动之前的及时
        long beg = System.currentTimeMillis();

        //接下来启动两个线程
        thread1.start();
        thread2.start();

        // 不能在这里结束计时
        // 在这里结束一定是有错误的 直接结束的话运行结果是0ms
        // 解释：在这个代码中看似是两个线程 实际是三个线程，分别是（main、thread1、thread2） 这三个线程是并发执行的关系
        // 就是main 在执行完两次start之后，就会立即执行结束计时。
        // 就好比，跑步比赛，main是裁判，thread1和thread2是选手，
        // 裁判这边吹哨，thread1和thread2就开始跑
        // 然而还没有到终点呢，裁判就停表了 （所以一定要保证thread1和thread2跑完之后在停表）

        // 具体怎么实现呢 就是前面讲的join 让main 等待一会，等到都执行完了在进行计算
        try {
            thread1.join();
            thread2.join();  // 这里的join都是main 来调用的因为concurrency放在main 方法里的
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        //结束计时
        long end = System.currentTimeMillis();
        System.out.println("并发执行的时间是："+(end-beg)+"ms");
    }


}
